#ifndef __BUFFER_HPP__
#define __BUFFER_HPP__

/* Buffer模块：用户态连接的缓冲区，包括发送缓冲区和接收缓冲区。
    1. 将数据写入到缓冲区
    2. 从缓冲区中读取数据
 */

static const size_t default_size = 1024;// 缓冲区默认大小

class Buffer
{
private:
    std::vector<char> _buffer;// 缓冲区维护的数组
    uint64_t _read_idx;// 可读区间的起始位置
    uint64_t _write_idx;// 可写区间的起始位置
private:
    /**
     * @brief 获取数组的起始位置
     * 
     * @return char* 指向数组首元素的指针
     */
    char* Begin() const
    {
        return (char*)&(*_buffer.begin());
    }

    /**
     * @brief 获取数组尾部空闲空间大小
     * 
     * @return uint64_t 
     */
    uint64_t TailEmptySize() const
    {
        return _buffer.size() - _write_idx;
    }
    /**
     * @brief 获取数组首部空闲空间大小
     * 
     * @return uint64_t 
     */
    uint64_t HeadEmptySize() const
    {
        return _read_idx;
    }

    /**
     * @brief 保证缓冲区可以写入len大小的数据，单位Byte
     * 
     * @param len 要写入的数据的大小
     */
    void EnsureWriteAble(uint64_t len)
    {
        if(len <= TailEmptySize()) return;

        if(HeadEmptySize() + TailEmptySize() >= len)
        {
            uint64_t read_able_size = ReadAbleSize();// 修正bug
            std::copy(ReadPosition(), WritePosition(), Begin());
            _read_idx = 0;
            _write_idx = _read_idx + read_able_size;// 修正bug
            // _write_idx = _read_idx + ReadAbleSize();// bug：_read_idx改变后，ReadAbleSize()也变了
        }
        // 扩容
        else
        {
#if 1 // 写法1
            DBG_LOG("BUFFER RESIZE: %ld", ReadAbleSize() + len);
            _buffer.resize(ReadAbleSize() + len);// bug：此种写法，必须再次调用一次EnsureWriteAble()，使得尾部空闲空间>=len。\
            否则尾部空闲空间可能并不足len，这样在写入数据时就会出现段错误
            EnsureWriteAble(len);// 修正bug
#else // 写法2
            _buffer.resize(_write_idx + len);// 修正bug
#endif
        }
    }

    /**
     * @brief 查找\n换行或\r\n回车换行
     * 
     * @param crlfSize 换行分割符(\n换行或\r\n回车换行)的大小(1或2)
     * @return char* 查找到的\n或\r的地址
     */
    char* FindCRLF(int8_t* crlfSize) const
    {
        char* pos = (char*)memchr(ReadPosition(), '\n', ReadAbleSize());
        if(pos == nullptr) return nullptr;

        *crlfSize = 1;
        if(pos > ReadPosition() && *(pos - 1) == '\r')
        {
            --pos;
            ++(*crlfSize);
        }

        return pos;
    }
    /**
     * @brief 移动缓冲区的可写起始位置，后移len长度
     * 断言保证移动距离是合法的，不会越界
     * 
     * @param len 移动距离
     */
    void MoveWriteOffset(uint64_t len)
    {
        assert(len <= TailEmptySize());
        _write_idx += len;
    }
public:
    Buffer(): _buffer(default_size), _read_idx(0), _write_idx(0)
    {}

    /**
     * @brief 移动缓冲区的可读起始位置，后移len长度
     * 断言保证移动距离是合法的，不会越界
     * 
     * @param len 移动距离
     */
    void MoveReadOffset(uint64_t len)
    {
        assert(len <= ReadAbleSize());
        _read_idx += len;
    }

    /**
     * @brief 获取缓冲区的起始可读位置
     * 
     * @return char* 指向起始可读位置数据的指针
     */
    char* ReadPosition() const
    {
        return Begin() + _read_idx;
    }
    /**
     * @brief 获取缓冲区的起始可写位置
     * 
     * @return char* 指向起始可写位置数据的指针
     */
    char* WritePosition() const
    {
        return Begin() + _write_idx;
    }
    /**
     * @brief 获取可读数据的大小/数量
     * 
     * @return uint64_t 
     */
    uint64_t ReadAbleSize() const
    {
        return _write_idx - _read_idx;
    }

    /**
     * @brief 从缓冲区中读取数据——读取len大小的数据到out中
     * 断言保证数据长度是合法的，不会越界
     * 
     * @param out 输出型参数 读取到的数据保存在这里
     * @param len 读取到的数据的大小，单位Byte
     */
    void Read(void* out, uint64_t len) const
    {
        assert(len <= ReadAbleSize());// 保证读取的数据合法

        if(len == 0) return;

        std::copy(ReadPosition(), ReadPosition() + len, (char*)out);
    }
    /**
     * @brief 从缓冲区中读取数据并拿出——读取len大小的数据到buffer中
     * 断言保证数据长度是合法的，不会越界
     * 
     * @param out 输出型参数 读取到的数据保存在这里
     * @param len 读取到的数据的大小，单位Byte
     */
    void ReadAndPop(void* out, uint64_t len)
    {
        Read(out, len);
        MoveReadOffset(len);
    }
    /**
     * @brief 从缓冲区中读取数据到string中——读取len大小的数据到buffer中
     * 断言保证数据长度是合法的，不会越界
     * 
     * @param out 输出型参数 读取到的数据保存在这里
     * @param len 读取到的数据的大小，单位Byte
     */
    void ReadAsString(std::string& out, uint64_t len) const
    {
        assert(len <= ReadAbleSize());
        // DBG_LOG("STRING RESIZE: %ld", len);
        out.resize(len);
        Read(&out[0], len);
        // DBG_LOG("STRING: %s", out.c_str());
    }
    /**
     * @brief 从缓冲区中读取数据到string中——读取len大小的数据到buffer中
     * 断言保证数据长度是合法的，不会越界
     * 
     * @param len 读取到的数据的大小，单位Byte
     * @return std::string 读取到的数据
     */
    std::string ReadAsString(uint64_t len) const
    {
        assert(len <= ReadAbleSize());
        std::string out;
        out.resize(len);
        Read(&out[0], len);
        return out;
    }
    /**
     * @brief 从缓冲区中读取数据到string中并拿出——读取len大小的数据到buffer中
     * 断言保证数据长度是合法的，不会越界
     * 
     * @param out 输出型参数 读取到的数据保存在这里
     * @param len 读取到的数据的大小，单位Byte
     */
    void ReadAsStringAndPop(std::string& out, uint64_t len)
    {
        DBG_LOG("ReadAsStringAndPop Call ReadAsString");
        ReadAsString(out, len);
        MoveReadOffset(len);
    }
    /**
     * @brief 从缓冲区中读取数据到string中并拿出——读取len大小的数据到buffer中
     * 断言保证数据长度是合法的，不会越界
     * 
     * @param out 输出型参数 读取到的数据保存在这里
     * @param len 读取到的数据的大小，单位Byte
     */
    std::string ReadAsStringAndPop(uint64_t len)
    {
        DBG_LOG("ReadAsStringAndPop Call ReadAsString");
        
        auto out = ReadAsString(len);
        MoveReadOffset(len);
        return out;
    }

    /**
     * @brief 将in中的len个数据写入到缓冲区_buffer中
     * 
     * @param in 输入型参数 要写入的数据
     * @param len 数据长度
     */
    void Write(const void* in, uint64_t len)
    {
        if(len == 0) return;// 保证写入合法数据

        // 1. 保证有足够的空闲空间可写
        EnsureWriteAble(len);
        // 2. 写数据
        std::copy((const char*)in, (const char*)in + len, WritePosition());
        // 3. 写位置偏移
        MoveWriteOffset(len);
    }
    /**
     * @brief 将string类型in中的数据写入到缓冲区_buffer中
     * 
     * @param in 输入型参数 要写入的数据
     */
    void WriteString(const std::string& in)
    {
        Write(in.c_str(), in.size());
    }
    /**
     * @brief 将Buffer类型in中的len个数据写入到缓冲区_buffer中
     * 
     * @param buffer 输入型参数
     */
    void WriteBuffer(const Buffer& buffer)
    {
        Write(buffer.ReadPosition(), buffer.ReadAbleSize());
    }

    /**
     * @brief 获取一行数据。以\n或\r\n为分隔符
     * 
     * @return std::string 获取到的一行数据，包含\r\n或\n符；\
     * @return 不足一行数据，返回空串
     */
    std::string GetLine() const
    {
        std::string line;
        int8_t crlfSize = 0;

        char* pos = FindCRLF(&crlfSize);
        if(pos == nullptr) return "";

        // DBG_LOG("GetLine Call ReadAsString");
        ReadAsString(line, pos - ReadPosition() + crlfSize);

        return line;
    }

    /**
     * @brief 获取一行数据，并从缓冲区中移除。以\n或\r\n为分隔符
     * 
     * @return std::string 获取到的一行数据，包含\r\n或\n符；\
     * @return 不足一行数据，返回空串
     */
    std::string GetLineAndPop()
    {
        std::string line = GetLine();
        MoveReadOffset(line.size());
        return line;
    }

    /**
     * @brief 清空缓冲区
     * 逻辑清空，将可读起始位置和可写起始位置都置为0
     * 
     */
    void Clear()
    {
        _read_idx = _write_idx = 0;
    }

    /**
     * @brief 获取缓冲区大小容量
     * 即内部数组的大小
     * 
     * @return size_t 
     */
    size_t Capacity() const
    {
        return _buffer.size();
    }
};

#endif